package com.panfeiyun.notes.example.tool.MyMybatisPlusGenerator;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;

public class MyGenerator {

    //自定义属性
    private Boolean clearFileDir = false;

    //属性集合
    private Properties properties = new Properties();

    //全局配置
    private String globalConfig_outputDir = "./";
    private Boolean globalConfig_fileOverride = false;
    private String globalConfig_author;
    private Boolean globalConfig_swagger2 = false;
    private Boolean globalConfig_baseResultMap = false;
    private Boolean globalConfig_baseColumnList = false;
    private Boolean globalConfig_enableCache = false;
    //各层文件名称方式，例如： %sAction 生成 UserAction  %s 为占位符
    private String globalConfig_entityName;//"%sEntity"
    private String globalConfig_mapperName;//"%sMapper"
    private String globalConfig_xmlName;//"%sMapper"
    private String globalConfig_serviceName;//"%sService"
    private String globalConfig_serviceImplName;//"%sServiceImpl"
    private String globalConfig_controllerName;//"%sController"

    //数据源配置
    private String database_url;
    private String database_driverName;
    private String database_username;
    private String database_password;

    //包配置
    private String package_parent = "com.example";//父包名。如果为空，将下面子包名必须写全部， 否则就只需写子包名
    private String package_moduleName;//父包模块名
    private String package_entity = "entity";//Entity包名
    private String package_service = "service";//Service包名
    private String package_serviceImpl = "service.impl";//Service Impl包名
    private String package_mapper = "mapper";//Mapper包名
    private String package_xml = "mapper.xml";//Mapper XML包名
    private String package_controller = "controller";//Controller包名

    //自定义模板配置
    private String customize_template_templatePathPrefix = "";
    private Boolean customize_template_controller = false;
    private String customize_template_controller_templatePath = "/templates/controller.java.ftl";
    private Boolean customize_template_service = false;
    private String customize_template_service_templatePath = "/templates/service.java.ftl";
    private Boolean customize_template_serviceImpl = false;
    private String customize_template_serviceImpl_templatePath = "/templates/serviceImpl.java.ftl";
    private Boolean customize_template_entity = false;
    private String customize_template_entity_templatePath = "/templates/entity.java.ftl";
    private Boolean customize_template_mapper = false;
    private String customize_template_mapper_templatePath = "/templates/mapper.java.ftl";
    private Boolean customize_template_xml = false;
    private String customize_template_xml_templatePath = "/templates/mapper.xml.ftl";
    private Integer customize_template_xml_pathModel = 0;
    private String customize_template_xml_path = "mapperXml";

    //策略配置项
    private String strategyConfig_tables = null;
    private String strategyConfig_tables_exclude = null;
    private String strategyConfig_versionFieldName;
    private String strategyConfig_logicDeleteFieldName;
    private String[] strategyConfig_tablePrefix;
    private String[] strategyConfig_fieldPrefix;
    private Boolean strategyConfig_entityColumnConstant = false;
    private Boolean strategyConfig_entityBuilderModel = false;
    private Boolean strategyConfig_entityLombokModel = false;
    private Boolean strategyConfig_entityBooleanColumnRemoveIsPrefix = false;
    private Boolean strategyConfig_restControllerStyle = false;
    private Boolean strategyConfig_controllerMappingHyphenStyle = false;
    private Boolean strategyConfig_entityTableFieldAnnotationEnable = false;

    //自定义增强功能
    private Boolean pfyPlugin_batch = false;
    private Boolean pfyPlugin_selective = false;

    public MyGenerator(String path) {
        //读取配置
        getProperties(path);
        setFields();
    }

    //代码生成方法
    public void generator() {

        //清除原文件
        if (clearFileDir) {
            delFileDir(new File(globalConfig_outputDir));
        }
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        全局配置(mpg, properties);
        // 数据源配置
        数据源配置(mpg, properties);
        // 包配置
        包配置(mpg, properties);
        // 自定义配置
        List<FileOutConfig> focList = 自定义配置(mpg, properties);
        // 配置模板
        TemplateConfig templateConfig = 配置模板(mpg, properties);
        //自定义文件生成配置
        自定义文件生成配置(focList, templateConfig, properties);
        // 策略配置
        策略配置(mpg, properties);
        //设置模板引擎
        设置模板引擎(mpg);
        //生成文件
        mpg.execute();
    }

    //自定义文件生成配置
    private void 自定义文件生成配置(List<FileOutConfig> focList, TemplateConfig templateConfig, Properties properties) {
        if (customize_template_controller) {
            templateConfig.setController(null);
            // 模板路径
            String controllerTemplatePath = customize_template_templatePathPrefix + customize_template_controller_templatePath;
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(controllerTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return getOutFilePath(package_controller, tableInfo.getControllerName(), StringPool.DOT_JAVA);
                }
            });
        }
        if (customize_template_service) {
            templateConfig.setService(null);
            // 模板路径
            String serviceTemplatePath = customize_template_templatePathPrefix + customize_template_service_templatePath;
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(serviceTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return getOutFilePath(package_service, tableInfo.getServiceName(), StringPool.DOT_JAVA);
                }
            });
        }
        if (customize_template_serviceImpl) {
            templateConfig.setServiceImpl(null);
            // 模板路径
            String serviceImplTemplatePath = customize_template_templatePathPrefix + customize_template_serviceImpl_templatePath;
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(serviceImplTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return getOutFilePath(package_serviceImpl, tableInfo.getServiceImplName(), StringPool.DOT_JAVA);
                }
            });
        }
        if (customize_template_entity) {
            templateConfig.setEntity(null);
            // 模板路径
            String entityTemplatePath = customize_template_templatePathPrefix + customize_template_entity_templatePath;
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(entityTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return getOutFilePath(package_entity, tableInfo.getEntityName(), StringPool.DOT_JAVA);
                }
            });
        }
        if (customize_template_mapper) {
            templateConfig.setMapper(null);
            // 模板路径
            String mapperTemplatePath = customize_template_templatePathPrefix + customize_template_mapper_templatePath;
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(mapperTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    return getOutFilePath(package_mapper, tableInfo.getMapperName(), StringPool.DOT_JAVA);
                }
            });
        }
        if (customize_template_xml) {
            templateConfig.setXml(null);
            // 模板路径
            String xmlTemplatePath = customize_template_templatePathPrefix + customize_template_xml_templatePath;
            // 自定义配置会被优先输出
            focList.add(new FileOutConfig(xmlTemplatePath) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                    switch (customize_template_xml_pathModel) {
                        case 1:
                            return globalConfig_outputDir
                                    + "/resources/"
                                    + getPackageToPath(package_parent, true)
                                    + getPackageToPath(package_moduleName, true)
                                    + getPackageToPath(package_xml, true)
                                    + tableInfo.getXmlName()
                                    + StringPool.DOT_XML;
                        case 2:
                            return globalConfig_outputDir
                                    + "/resources/"
                                    + getPackageToPath(customize_template_xml_path, true)
                                    + getPackageToPath(package_moduleName, true)
                                    + tableInfo.getXmlName()
                                    + StringPool.DOT_XML;
                        case 0:
                        default:
                            return getOutFilePath(package_xml, tableInfo.getXmlName(), StringPool.DOT_XML);
                    }
                }
            });
        }
    }

    //设置模板引擎
    private void 设置模板引擎(AutoGenerator mpg) {
        // set velocity engine
//        mpg.setTemplateEngine(new VelocityTemplateEngine());
        // set freemarker engine
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        // set beetl engine
//        mpg.setTemplateEngine(new BeetlTemplateEngine());
    }

    //策略配置
    private void 策略配置(AutoGenerator mpg, Properties properties) {
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        if (StringUtils.isNotEmpty(strategyConfig_tables)) {
            strategy.setInclude(strategyConfig_tables.split(","));//设置包括的表
        } else {
            if (StringUtils.isNotEmpty(strategyConfig_tables_exclude)) {
                strategy.setExclude(strategyConfig_tables_exclude.split(","));//设置排除的表
            }
        }
        strategy.setVersionFieldName(strategyConfig_versionFieldName);
        strategy.setLogicDeleteFieldName(strategyConfig_logicDeleteFieldName);
        strategy.setTablePrefix(strategyConfig_tablePrefix);
        strategy.setFieldPrefix(strategyConfig_fieldPrefix);
        strategy.setEntityColumnConstant(strategyConfig_entityColumnConstant);
        strategy.setEntityBuilderModel(strategyConfig_entityBuilderModel);
        strategy.setEntityLombokModel(strategyConfig_entityLombokModel);//是否为lombok模型
        strategy.setEntityBooleanColumnRemoveIsPrefix(strategyConfig_entityBooleanColumnRemoveIsPrefix);
        strategy.setRestControllerStyle(strategyConfig_restControllerStyle);
        strategy.setControllerMappingHyphenStyle(strategyConfig_controllerMappingHyphenStyle);
        strategy.setEntityTableFieldAnnotationEnable(strategyConfig_entityTableFieldAnnotationEnable);
        mpg.setStrategy(strategy);
    }

    //配置模板
    private TemplateConfig 配置模板(AutoGenerator mpg, Properties properties) {
        TemplateConfig templateConfig = new TemplateConfig();
        mpg.setTemplate(templateConfig);
        return templateConfig;
    }

    //自定义配置
    private List<FileOutConfig> 自定义配置(AutoGenerator mpg, Properties properties) {
        //自定义配置对象
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                //设置属性
                Map<String, Object> map = new HashMap<>();
                map.put("pfyPlugin_batch", pfyPlugin_batch);
                map.put("pfyPlugin_selective", pfyPlugin_selective);
                this.setMap(map);
            }
        };
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        return focList;
    }

    // 包配置
    private void 包配置(AutoGenerator mpg, Properties properties) {
        PackageConfig pc = new PackageConfig();
        pc.setParent(package_parent);
        String moduleName = package_moduleName;
        if (StringUtils.isNotEmpty(moduleName)) {
            pc.setModuleName(moduleName);
        }
        pc.setEntity(package_entity);
        pc.setService(package_service);
        pc.setServiceImpl(package_serviceImpl);
        pc.setMapper(package_mapper);
        pc.setXml(package_xml);
        pc.setController(package_controller);
        mpg.setPackageInfo(pc);
    }

    // 数据源配置
    private void 数据源配置(AutoGenerator mpg, Properties properties) {
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(database_url);
        dsc.setDriverName(database_driverName);
        dsc.setUsername(database_username);
        dsc.setPassword(database_password);
        mpg.setDataSource(dsc);
    }

    // 全局配置
    private void 全局配置(AutoGenerator mpg, Properties properties) {
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(globalConfig_outputDir);
        gc.setFileOverride(globalConfig_fileOverride);
        gc.setAuthor(globalConfig_author);
        gc.setSwagger2(globalConfig_swagger2); //是否添加swagger
        gc.setBaseResultMap(globalConfig_baseResultMap); //是否添加通用查询映射结果
        gc.setBaseColumnList(globalConfig_baseColumnList); //是否添加通用查询结果列
        gc.setEnableCache(globalConfig_enableCache); //是否添加二级缓存
        gc.setEntityName(globalConfig_entityName);
        gc.setMapperName(globalConfig_mapperName);
        gc.setXmlName(globalConfig_xmlName);
        gc.setServiceName(globalConfig_serviceName);
        gc.setServiceImplName(globalConfig_serviceImplName);
        gc.setControllerName(globalConfig_controllerName);
        gc.setOpen(false);
        gc.setDateType(DateType.ONLY_DATE);
        mpg.setGlobalConfig(gc);
    }

    //获取配置文件
    private void getProperties(String path) {
        try (
                InputStream in = new FileInputStream(path);
        ) {
            properties.load(in);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(2);
        }
    }

    //删除文件夹
    private void delFileDir(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
                delFileDirCycle(file1);
            }
        }
    }

    //循环删除文件及文件夹
    private void delFileDirCycle(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File file1 : files) {
                delFileDirCycle(file1);
            }
        }
        file.delete();
    }

    //设置单个属性
    private void setField(String fieldName, String propertyName) {
        try {
            Class<? extends MyGenerator> clazz = this.getClass();
            Field field = clazz.getDeclaredField(fieldName);
            setField(field, propertyName);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(2);
        }
    }

    //设置单个属性
    private void setField(Field field, String propertyName) {
        try {
            field.setAccessible(true);
            Class<?> fieldType = field.getType();
            String value = properties.getProperty(propertyName);
            if (null != value && !"null".equals(value)) {
                if ("java.lang.String".equals(fieldType.getName())) {
                    field.set(this, value);
                } else {
                    field.set(this, fieldType.getMethod("valueOf", String.class).invoke(null, value));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(2);
        }
    }

    //设置所有属性
    private void setFields() {
        Class<? extends MyGenerator> clazz = this.getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String fieldName = declaredField.getName();
            if ("properties".equals(fieldName)) continue;
            String propertyKey = "pfy.mybatisPlus.generator." + fieldName.replace("_", ".");
            setField(declaredField, propertyKey);
        }
    }

    //将包转化为路径
    private String getPackageToPath(String packageStr, boolean replace) {
        if (StringUtils.isNotEmpty(packageStr)) {
            if (replace) {
                return packageStr.replaceAll("\\.", "/") + "/";
            } else {
                return packageStr + "/";
            }
        } else {
            return "";
        }
    }

    //生成文件路径
    private String getOutFilePath(String typePackage, String fileName, String fileSuffix) {
        return globalConfig_outputDir
                + "/"
                + getPackageToPath(package_parent, true)
                + getPackageToPath(package_moduleName, true)
                + getPackageToPath(typePackage, true)
                + fileName
                + fileSuffix;
    }

}
